<?php


namespace app\unify\controller;


use app\BaseController;
use app\unify\model\ApiAuth;
use ReflectionClass;
use think\annotation\route\Validate;
use think\db\exception\DbException;
use think\db\Query;
use think\facade\Db;
use think\Paginator;
use think\Request;
use app\unify\validate\Api as ApiValidate;
use app\unify\model\Api as ApiModel;
use app\unify\model\Auth as AuthModel;

class Api extends BaseController
{

    /**
     *
     * 接口列表
     * @param string $path
     * @param string $platform
     * @param string $keywords
     * @param array $anonymous
     * @param array $createdType
     * @param array $authIds
     * @param int $page
     * @param int $limit
     * @return Paginator
     * @throws DbException
     */
    public function index(
        string $path = null,
        array $platform = [],
        string $keywords = null,
        array  $anonymous = [],
        array  $createdType = [],
        array  $authIds = [],
        array  $status = [],
        int    $page = 1,
        int    $limit = 15
    ): Paginator
    {
        $where = [];

        if (!empty($path)) {
            $where[] = ['path', 'LIKE', "%{$path}%"];
        }

        if (!empty($platform)) {
            $where[] = function (Query $q) use ($platform) {
                foreach ($platform as $item) {
                    $q->whereOrRaw("FIND_IN_SET('{$item}',`platform`)");
                }
            };
        }

        if ($keywords) {
            $where[] = ['path|description', 'LIKE', "%{$keywords}%"];
        }

        if (!empty($anonymous)) {
            $where[] = ['anonymous', 'in', $anonymous];
        }

        if (!empty($createdType)) {
            $where[] = ['created_type', 'in', $createdType];
        }

        if ($authIds) {
            $where[] = ['id', 'in', ApiAuth::where([
                ['auth_id', 'in', $authIds]
            ])->column("api_id")];
        }

        if (!empty($status)) {
            $where[] = ['status', 'in', $status];
        }

        return ApiModel::where($where)->order("id DESC")->append(['createdTypeName', 'statusName', 'platformName', 'allowName', 'anonymousName'])->with(['auths'])->paginate([
            'page' => $page,
            'list_rows' => $limit
        ])->each(function ($item) {
            $item['authNames'] = implode("、", $item['auths']->map(fn($auth) => $auth['name'])->toArray());
            if (empty($item['authNames'])) {
                $item['authNames'] = "无限制";
            }
            unset($item['auths']);
        });
    }

    /**
     * 筛选信息
     * @param mixed $id
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function form(mixed $id = 0): array
    {
        $data = [
            'entity' => [
                "anonymous" => ApiModel::ANONYMOUS_FALSE,
                'platform' => ApiModel::PLATFORM_ADMIN,
            ],
            'anonymous' => ApiModel::ANONYMOUS_LIST,
            'createdType' => ApiModel::CREATED_TYPE_LIST,
            'authIds' => array_column(AuthModel::field('id,name')->select()->toArray(), 'name', 'id'),
            'platform' => ApiModel::PLATFORM_LIST,
            'allow' => ApiModel::ALLOW_LIST,
            'status' => ApiModel::STATUS_LIST,
        ];

        if ($id) {
            $entity = ApiModel::findOrEmpty($id);
            $entity['authList'] = $entity->auths->map(fn($item) => strval($item->pivot->auth_id));
            $entity['platform'] = explode(",", $entity['platform'] ?: '');
            $data['entity'] = $entity;
        }

        return $data;
    }

    /**
     * @Notes 更新API列表
     * @Author EdwardCho
     * @Date 2023/12/29
     * @Time 18:34
     * @Interface sync
     * @throws \ReflectionException
     */
    public function sync()
    {
        $directory = $this->app->getBasePath() . '**/controller/*.php';
        $files = glob($directory);

        $dataList = [];

        foreach ($files as $file) {
            $module = basename(dirname($file, 2));
            $controller = pathinfo($file, PATHINFO_FILENAME);
            $class = "app\\{$module}\\controller\\" . $controller;
            $reflectionClass = new ReflectionClass($class);

            foreach ($reflectionClass->getMethods() as $method) {
                if (!$method->isPublic() || $method->isConstructor()) {
                    continue;
                }

                $originDoc = $method->getDocComment();
                // 删除@开头的行
                $doc = preg_replace('/^\s*\*\s*@.*$/m', '', $originDoc);
                $doc = trim(str_replace(["\r", "\n", " ", "/", "*"], '', $doc));
                if (empty($doc)) {
                    preg_match('/@Notes\s*([^*]+)/', $originDoc, $matches);
                    if (isset($matches[1])) {
                        $doc = trim($matches[1]);
                    }
                }

                $dataList[] = [
                    'path' => "/{$module}/{$controller}/{$method->getName()}",
                    'description' => $doc,
                    'created_type' => ApiModel::CREATED_TYPE_SYNC,
                    'status' => ApiModel::STATUS_ENABLE,
                ];
            }
        }

        Db::transaction(function () use ($dataList) {
            foreach ($dataList as $data) {
                $api = ApiModel::where([
                    'path' => $data['path']
                ])->findOrEmpty();
                if ($api->isEmpty()) {
                    ApiModel::create($data);
                } else {
                    $api->save($data);
                }
            }

            //设置其他找不到的接口设置为失效状态。
            ApiModel::where([
                ['path', 'not in', array_column($dataList, 'path')],
                ['created_type', '<>', ApiModel::CREATED_TYPE_MANUAL],
            ])->update([
                'status' => ApiModel::STATUS_DISABLE
            ]);

        });


        return '同步成功';
    }

    /**
     * @Notes 导出APIJSON
     * @Author EdwardCho
     * @Date 2024/1/4
     * @Time 14:28
     * @Interface exportOpenApiJSON
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function exportJSON()
    {
        $paths = [];
        ApiModel::select()->each(function ($api) use (&$paths) {
            $paths[$api['path']] = [
                'post' => [
                    'summary' => $api['description'],
                ]
            ];
        });
        $openapi = [
            'openapi' => '3.0.0',
            'info' => [
                'title' => config('base.site_name'),
                'version' => config('sys.version'),
                'description' => config('base.site_description'),
            ],
            'paths' => $paths
        ];

        // 输出 JSON
        return [
            'openApi' => $openapi
        ];
    }

    /**
     * 保存API
     * @Notes 功能描述
     * @Author EdwardCho
     * @Date 2023/12/30
     * @Time 04:28
     * @Interface save
     * @param \think\Request $r
     * @return string
     */
    #[Validate(ApiValidate::class, scene: ApiValidate::SAVE)]
    public function save(Request $r)
    {
        Db::transaction(function () use ($r) {
            $data = $r->param();
            $data['createdType'] = ApiModel::CREATED_TYPE_MANUAL;

            if (!empty($data['platform'])) {
                $data['platform'] = implode(",", $data['platform']);
            }

            $api = ApiModel::create($data);
            if (!empty($data['authList'])) {
                $api->auths()->saveAll($data['authList']);
            }
        });

        return '修改成功';
    }

    /**
     * 修改API
     * @param \think\Request $request
     * @param int $id
     * @return string
     * @throws \Exception
     */
    #[Validate(ApiValidate::class, scene: ApiValidate::UPDATE)]
    public function update(Request $request, int $id): string
    {

        $api = ApiModel::findOrEmpty($id);

        if ($api->isEmpty()) {
            throw new \Exception('未找到该接口');
        }

        Db::transaction(function () use ($request, $api) {

            $params = $request->param();

            if (!empty($params['platform'])) {
                $params['platform'] = implode(",", $params['platform']);
            }

            $api->save($params);

            $api->auths()->detach();
            if (!empty($params['authList'])) {
                $api->auths()->saveAll($request->param('authList'));
            }
        });


        return '修改成功';
    }

    /**
     * 修改字段
     * @Author EdwardCho
     * @Date 2023/12/30
     * @Time 14:09
     * @Interface updateField
     * @param array $ids
     * @param $field
     * @param $value
     * @return string
     */
    #[Validate(ApiValidate::class, scene: ApiValidate::UPDATE_FIELD)]
    public function updateField(array $ids, $field, $value)
    {
        Db::transaction(function () use ($value, $field, $ids) {
            ApiModel::where([
                [ApiModel::getPrimaryKey(), 'in', $ids]
            ])->update([
                $field => $value
            ]);
        });

        return '操作成功';
    }

    /**
     * 删除API
     * @param array $ids
     * @return string
     */
    #[Validate(ApiValidate::class, scene: ApiValidate::DELETE)]
    public function delete(array $ids): string
    {
        Db::transaction(function () use ($ids) {
            ApiModel::destroy(function (Query $q) use ($ids) {
                $q->where([
                    [ApiModel::getPrimaryKey(), 'in', $ids]
                ]);
            });
        });

        return '删除成功';
    }
}